/*
\ * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.ranch.api.service.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.ranch.api.RanchConstants;
import org.ranch.api.service.AnimalSearchParameters;
import org.ranch.api.service.FarmService;
import org.ranch.dao.AnimalDAO;
import org.ranch.dao.FarmDAO;
import org.ranch.dao.KraalDAO;
import org.ranch.model.Address;
import org.ranch.model.Animal;
import org.ranch.model.Farm;
import org.ranch.model.Kraal;
import org.ranch.model.Person;
import org.ranch.model.RecordStatus;
import org.ranch.model.exception.RanchValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.genericdao.search.Filter;
import com.googlecode.genericdao.search.Search;

/**
 * Implementation of {@link FarmService}
 */
@Transactional
@Service("farmService")
public class FarmServiceImpl implements FarmService {

    @Autowired
    private FarmDAO farmDAO;

    @Autowired
    private KraalDAO kraalDAO;

    @Autowired
    private AnimalDAO animalDAO;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Animal animal) throws RanchValidationException {
	animalDAO.save(animal);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void delete(Animal animal) {
	animalDAO.delete(animal);
    }

    @Override
    @Transactional(readOnly = true)
    public Animal getAnimalById(String id) {
	return animalDAO.searchUniqueByPropertyEqual("id", id);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Animal> getAnimals() {
	return animalDAO.searchByRecordStatus(RecordStatus.ACTIVE);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Kraal kraal) throws RanchValidationException {
	kraalDAO.save(kraal);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void delete(Kraal Kraal) {
	kraalDAO.delete(Kraal);
    }

    @Override
    @Transactional(readOnly = true)
    public Kraal getKraalById(String id) {
	return kraalDAO.searchUniqueByPropertyEqual("id", id);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Kraal> getKraals() {
	return kraalDAO.searchByRecordStatus(RecordStatus.ACTIVE);
    }

    @Override
    public List<Kraal> getKraalsByPage(Integer pageNo) {
	return null;
    }

    @Override
    public List<Kraal> searchKraalsByTitle(String query) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public List<Kraal> searchKraalsByTitle(String query, Integer pageNumber) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getNumberOfKraalsInSearch(String query, Integer pageNumber) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getNumberOfKraals() {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Farm farm) throws RanchValidationException {
	farmDAO.save(farm);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void delete(Farm farm) {
	farmDAO.delete(farm);
    }

    @Override
    @Transactional(readOnly = true)
    public Farm getFarmById(String id) {
	return farmDAO.searchUniqueByPropertyEqual("id", id);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Farm> getFarms() {
	List<Farm> farms = farmDAO.searchByRecordStatus(RecordStatus.ACTIVE);
	Collections.sort(farms);
	return farms;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void purgeFarm(Farm farm) {
	farmDAO.remove(farm);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Farm> getFarms(Integer pageNo) {
	Search search = new Search();
	search.setMaxResults(RanchConstants.MAX_NUM_PAGE_RECORD);
	search.addSort("name", false, true);
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);

	/*
	 * if the page number is less than or equal to zero, no need for paging
	 */
	if (pageNo != null && pageNo > 0) {
	    search.setPage(pageNo - 1);
	} else {
	    search.setPage(0);
	}

	List<Farm> farms = farmDAO.search(search);
	Collections.sort(farms);
	return farms;
    }

    @Override
    @Transactional(readOnly = true)
    public void deleteFarm(List<String> farmIds) {
	if (farmIds != null) {
	    for (String farmId : farmIds) {
		Farm farm = farmDAO.find(farmId);
		if (farm != null) {
		    farmDAO.remove(farm);
		}
	    }
	}
    }

    @Override
    public int getTotalNumberOfFarm() {
	Search search = new Search();
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	return farmDAO.count(search);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Farm> getFarmThatStartWith(String name) {
	Search search = new Search();
	search.addFilter(new Filter("name", name, Filter.OP_LIKE));
	search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	List<Farm> farms = farmDAO.search(search);

	Collections.sort(farms);
	return farms;
    }

    @Override
    public void saveFarmAnimal(Farm farm, Animal animal) throws RanchValidationException {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean saveFarmKraal(Kraal kraal) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean deleteFarmKraal(Kraal Kraal) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void deletePerson(Person person) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void deletePersons(List<Person> persons) {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean saveFarmAddress(Farm staff, Address address) throws RanchValidationException {
	throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    @Transactional(readOnly = true)
    public List<Animal> searchAnimalByTagNo(String query, String kraalIds) {
	/*
	 * String param = new
	 * StringBuilder().append("%").append(StringEscapeUtils
	 * .escapeSql(query)) .append("%").toString(); Search search = new
	 * Search(); search.addFilterEqual("recordStatus", RecordStatus.ACTIVE);
	 * search.addFilterLike("earTag", param);
	 */

	List<Animal> animals = animalDAO.searchAnimalByTagNo(query, kraalIds);
	Collections.sort(animals);
	return animals;
    }

    @Override
    public long getNumberOfAnimalsInSearch(String query) {
	return 0;
    }

    @Override
    public void validateFarm(Farm farm) throws RanchValidationException {
	if (StringUtils.isBlank(farm.getName()))
	    throw new RanchValidationException("Supplied Farm is missing a name");

	if (farm.getManager() == null)
	    throw new RanchValidationException("Supplied Farm missing a manager");

	if (StringUtils.isBlank(farm.getAddress().getPhoneNo()))
	    throw new RanchValidationException("Supplied Farm missing a phoneNo address");
    }

    @Override
    public void validateKraal(Kraal kraalOldModel) throws RanchValidationException {
	if (StringUtils.isBlank(kraalOldModel.getName()))
	    throw new RanchValidationException("Supplied Ranch missing a name");
    }

    @Override
    public void validateAnimal(Animal animal) throws RanchValidationException {
	if (animal.getName() == null)
	    throw new RanchValidationException("Animal Name is missing");

	if (animal.getType() == null)
	    throw new RanchValidationException("Animal Type is missing");

	if (animal.getStatus() == null)
	    throw new RanchValidationException("Animal Status is missing");

	if (StringUtils.isEmpty(animal.getEarTag()))
	    throw new RanchValidationException("Animal ear tag is missing");

	/*
	 * if (animal.getWeight() == 0) throw new
	 * RanchValidationException("Animal weight cannot be Zero");
	 */
	if (animal.getWeight() < 0)
	    throw new RanchValidationException("Animal weight cannot be less than Zero");

	if (StringUtils.isEmpty(animal.getName()))
	    throw new RanchValidationException("Animal is missing name");

	if (animal.getColour() == null)
	    throw new RanchValidationException("Animal Color is missing");

	if (animal.getBreed() == null)
	    throw new RanchValidationException("Animal Breed is missing");

	/*
	 * if (animal.getBirthWeight() == 0) throw new
	 * RanchValidationException("Animal birth weight cannot be Zero");
	 */

	if (animal.getBirthWeight() < 0)
	    throw new RanchValidationException("Animal birth weight cannot be less than Zero");

	if (animal.getDateOfBirth() != null) {
	    if (animal.getDateOfBirth().after(Calendar.getInstance().getTime())) {
		throw new RanchValidationException("Animal date of birth cannot be in the future");
	    }
	}
	if (animal.getPurchaseDate() != null) {
	    if (animal.getPurchaseDate().after(Calendar.getInstance().getTime())) {
		throw new RanchValidationException("Animal purchase date cannot be in the future");
	    }
	}

	if (StringUtils.isNotEmpty(animal.getEarTag()) && StringUtils.isEmpty(animal.getId())) {
	    if (getAnimalByEarTag(animal.getEarTag()) != null) {
		throw new RanchValidationException("Animal with this " + animal.getEarTag()
			+ " ear tag already exist");
	    }
	}

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.ranch.api.service.FarmService#searchAnimalByTagNo(java.lang.String)
     */
    @Override
    public List<Animal> searchAnimalByTagNo(String query) {
	return null;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Animal> getAnimals(Integer pageNo) {
	Search search = new Search();
	search.setMaxResults(RanchConstants.MAX_NUM_PAGE_RECORD);
	search.addSort("name", false, true);

	if (pageNo != null && pageNo > 0) {
	    search.setPage(pageNo - 1);
	} else {
	    search.setPage(0);
	}

	return animalDAO.search(search);
    }

    @Override
    @Transactional(readOnly = true)
    public long getTotalNumberOfAnimal() {
	return animalDAO.count(new Search());
    }

    @Override
    @Transactional(readOnly = true)
    public List<Animal> searchWithParams(AnimalSearchParameters params, Integer pageNo) {
	return animalDAO.searchWithParams(params, pageNo, RanchConstants.MAX_NUM_PAGE_RECORD);
    }

    @Override
    @Transactional(readOnly = true)
    public long numberOfAnimalWithSearchParams(AnimalSearchParameters params) {
	return animalDAO.numberOfAnimalWithSearchParams(params);
    }

    @Override
    @Transactional(readOnly = true)
    public InputStream exportToExcel(AnimalSearchParameters params) {
	List<Animal> animals = animalDAO.searchWithParams(params);
	Workbook workBook = new HSSFWorkbook();
	Sheet sheet = workBook.createSheet("Staff Members");

	createAnimalHeaderRow(sheet);

	/*
	 * row number here starts at one because of the header row.
	 */
	int rowNumber = 1;
	for (Animal animal : animals) {
	    rowNumber = createStaffDataRow(sheet, rowNumber, animal);
	}

	ByteArrayOutputStream stream = new ByteArrayOutputStream();

	try {
	    workBook.write(stream);
	} catch (IOException e) {
	}

	return new ByteArrayInputStream(stream.toByteArray());
    }

    /**
     * @param sheet
     * @param rowNumber
     * @param animal
     * @return
     */
    private int createStaffDataRow(Sheet sheet, int rowIndex, Animal animal) {
	Row dataRow = sheet.createRow(rowIndex++);
	dataRow.createCell(0).setCellValue(animal.getType().getName());
	dataRow.createCell(1).setCellValue(animal.getEarTag());
	dataRow.createCell(2).setCellValue(animal.getWeight());
	dataRow.createCell(3).setCellValue(animal.getName());
	dataRow.createCell(4).setCellValue(animal.getHealthStatus().getName());

	if (animal.getDateOfBirth() != null) {
	    dataRow.createCell(5).setCellValue(animal.getDateOfBirth());
	    CellStyle cellStyle = sheet.getWorkbook().createCellStyle();
	    cellStyle.setDataFormat(sheet.getWorkbook().getCreationHelper().createDataFormat()
			.getFormat("dd/MM/yyyy"));

	    dataRow.getCell(5).setCellStyle(cellStyle);
	} else {
	    dataRow.createCell(5).setCellValue("");
	}

	dataRow.createCell(6).setCellValue(animal.getColour().getName());
	dataRow.createCell(7).setCellValue(animal.getBreed().getName());
	dataRow.createCell(8).setCellValue(animal.getStatus().getName());
	dataRow.createCell(9).setCellValue(animal.getBuyingPrice());
	dataRow.createCell(10).setCellValue(animal.getSellingPrice());
	
	if(animal.getKraal() != null){
	    dataRow.createCell(11).setCellValue(animal.getKraal().getName());
	    dataRow.createCell(12).setCellValue(animal.getKraal().getFarm().getName());
		dataRow.createCell(13)
			.setCellValue(animal.getKraal().getFarm().getManager().getFullNames());
	}else{
	    dataRow.createCell(11).setCellValue("");
	    dataRow.createCell(12).setCellValue("");
	    dataRow.createCell(13).setCellValue("");
	}
	

	return rowIndex;
    }

    /**
     * @param sheet
     */
    private void createAnimalHeaderRow(Sheet sheet) {
	Row headerRow = sheet.createRow((short) 0);
	headerRow.createCell(0).setCellValue("Type");
	headerRow.createCell(1).setCellValue("EarTag");
	headerRow.createCell(2).setCellValue("Weigt");
	headerRow.createCell(3).setCellValue("Name");
	headerRow.createCell(4).setCellValue("HealthStatus");
	headerRow.createCell(5).setCellValue("DOB");
	headerRow.createCell(6).setCellValue("Color");
	headerRow.createCell(7).setCellValue("Breed");
	headerRow.createCell(8).setCellValue("Status");
	headerRow.createCell(9).setCellValue("Buying Price");
	headerRow.createCell(10).setCellValue("Selling Price");
	headerRow.createCell(11).setCellValue("Kraal");
	headerRow.createCell(12).setCellValue("Farm");
	headerRow.createCell(13).setCellValue("Manager");
    }

    @Override
    @Transactional(readOnly = true)
    public Animal getAnimalByEarTag(String earTag) {
	return animalDAO.searchUniqueByPropertyEqual("earTag", earTag, RecordStatus.ACTIVE);
    }

    @Override
    public Kraal getKraalByName(String name) {
	return kraalDAO.searchUniqueByPropertyEqual("name", name, RecordStatus.ACTIVE);
    }

}
